package com.orhanobut.hawk.serializer

import com.orhanobut.hawk.DataInfo
import com.orhanobut.hawk.log.LogInterceptor

class HawkSerializer(private val logInterceptor: LogInterceptor) : Serializer {
  private val DELIMITER = '@'
  private val INFO_DELIMITER = "#"
  private val NEW_VERSION = 'V'

  override fun <T> serialize(cipherText: String, originalGivenValue: T): String {

    var keyClassName = ""
    var valueClassName = ""
    val dataType: Char

    when (originalGivenValue) {
      is List<*> -> {
        val list = originalGivenValue
        if (list.isNotEmpty()) keyClassName = list[0]?.javaClass?.name ?: ""
        dataType = DataInfo.TYPE_LIST
      }
      is Map<*, *> -> {
        dataType = DataInfo.TYPE_MAP
        val map = originalGivenValue
        if (map.isNotEmpty()) {
          val entry = map.entries.first()
          keyClassName = entry.key?.javaClass?.name ?: ""
          valueClassName = entry.value?.javaClass?.name ?: ""
        }
      }
      is Set<*> -> {
        val set = originalGivenValue
        if (set.isNotEmpty()) keyClassName = set.first()?.javaClass?.name ?: ""
        dataType = DataInfo.TYPE_SET
      }
      else -> {
        dataType = DataInfo.TYPE_OBJECT
        keyClassName = originalGivenValue!!::class.java.name
      }
    }

    return keyClassName + INFO_DELIMITER +
      valueClassName + INFO_DELIMITER +
      dataType + NEW_VERSION + DELIMITER +
      cipherText
  }

  override fun deserialize(serializedText: String): DataInfo {
    val infos = serializedText.split(INFO_DELIMITER)

    val type = infos[2][0]

    var keyClazz: Class<*>? = null
    val firstElement = infos[0]
    if (firstElement.isNotEmpty()) {
      try {
        keyClazz = Class.forName(firstElement)
      } catch (e: ClassNotFoundException) {
        logInterceptor.w(e.message ?: "")
      }
    }

    var valueClazz: Class<*>? = null
    val secondElement = infos[1]
    if (secondElement.isNotEmpty()) {
      try {
        valueClazz = Class.forName(secondElement)
      } catch (e: ClassNotFoundException) {
        logInterceptor.w(e.message ?: "")
      }
    }

    val cipherText = getCipherText(infos.last())
    return DataInfo(type, cipherText, keyClazz, valueClazz)
  }

  private fun getCipherText(serializedText: String): String {
    val index = serializedText.indexOf(DELIMITER)
    if (index == -1) {
      logInterceptor.e("Text should contain delimiter")
      throw IllegalArgumentException("Text should contain delimiter")
    }
    return serializedText.substring(index + 1)
  }
}
